/**
 ***************************************************************************************************
 *
 * @file    main.c
 *
 * @brief   gattc app entry point
 *
 * Copyright (C) Eker 2020-2022
 *
 ***************************************************************************************************
 */

/**
 ***************************************************************************************************
 * @addtogroup APP
 * @{
 ***************************************************************************************************
 */

/*
 * INCLUDE FILES
 ***************************************************************************************************
 */
#include "rwapp_config.h"

#if (BLE_APP_CENTRAL_SUPPORT)
#include "app_task.h"
#include "ke_timer.h"
#include "co_bt.h"
#include "co_utils.h"
#include "prf_types.h"
#include "arch.h"
#include "prf.h"
#include <string.h>
#include "gattc_task.h"
#include "app_gattc.h"
#include "prf_utils.h"
#include "app_central.h"
#include "app.h"
#include "app_utils.h"

#define APP_LOG_DOMAIN      "GATTC"
#define APP_LOG_LEVEL       APP_GATTC_LOG_LEVEL
#include "app_log.h"

/*
 * DEFINES
 ***************************************************************************************************
 */
#define APP_GATTC_MTU_TO_NTF_WRTCMD_LEN(n)      ((n) - 3)
#define APP_GATTC_MTO_TO_NTF_WRTCMD_LEN(n)      ((n) - 3)
#define APP_GATTC_UPLOAD_MAX_LEN                BLE_MAX_OCTETS//(BLE_MAX_OCTETS-4-3)

/*
 * LOCAL FUNCTION DEFINITIONS
 ***************************************************************************************************
 */
/**
 ***************************************************************************************************
 * @brief Handles reception of the @ref GATTC_SDP_SVC_IND_HANDLER message.
 * The handler stores the found service details for service discovery.
 *
 * @param[in] msgid Id of the message received (probably unused).
 * @param[in] param Pointer to the parameters of the message.
 * @param[in] dest_id ID of the receiving task instance (probably unused).
 * @param[in] src_id ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ***************************************************************************************************
 */
__STATIC int app_gattc_sdp_svc_ind_handler(ke_msg_id_t const msgid,
                                           struct gattc_sdp_svc_ind const *ind,
                                           ke_task_id_t const dest_id,
                                           ke_task_id_t const src_id)
{
    LOG_VBS_FUNC();

    uint8_t conidx = KE_IDX_GET(src_id);
    uint8_t index = app_central_find_by_conidx(conidx);

    // find ccc handle and store it
    for (uint8_t idx=0; idx<(ind->end_hdl - ind->start_hdl); idx++)
    {
        // characteristic description
        if(ind->info[idx].att_type == GATTC_SDP_ATT_DESC)
        {
            uint16_t char_hdl = ind->start_hdl + 1 + idx;
            uint16_t uuid = ((ind->info[idx].att.uuid[1]<<8) | ind->info[idx].att.uuid[0]);
            LOG_DBG("desc:  0x%04x(%d), uuid: 0x%04x", char_hdl, char_hdl, uuid);
            if (uuid == ATT_DESC_CLIENT_CHAR_CFG)
            {
                app_env.central.connect[index].ccc_handle[app_env.central.connect[index].ccc_handle_num++] = char_hdl;
                LOG_DBG("CCC handle: 0x%02x(%d)", char_hdl, char_hdl);
            }
        }
    }

    return (KE_MSG_CONSUMED);
}

/**
 ***************************************************************************************************
 * @brief Handles reception of the @ref GATTC_CMP_EVT message.
 * This generic event is received for different requests, so need to keep track.
 *
 * @param[in] msgid Id of the message received (probably unused).
 * @param[in] param Pointer to the parameters of the message.
 * @param[in] dest_id ID of the receiving task instance (probably unused).
 * @param[in] src_id ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ***************************************************************************************************
 */
__STATIC int app_gattc_cmp_evt_handler(ke_msg_id_t const msgid,
                                       struct gattc_cmp_evt const *param,
                                       ke_task_id_t const dest_id,
                                       ke_task_id_t const src_id)
{
    LOG_VBS_FUNC();

    uint8_t conidx = KE_IDX_GET(src_id);
    uint8_t index = app_central_find_by_conidx(conidx);

    switch(param->operation)
    {
        case GATTC_READ:
            LOG_DBG("GATTC_READ");
            break;

        case GATTC_READ_LONG:
            LOG_DBG("GATTC_READ_LONG");
            break;

        case GATTC_READ_BY_UUID:
            LOG_DBG("GATTC_READ_BY_UUID");
            break;

        case GATTC_READ_MULTIPLE:
            LOG_DBG("GATTC_READ_MULTIPLE");
            break;

        case GATTC_WRITE:
            LOG_DBG("GATTC_WRITE");
            break;

        case GATTC_WRITE_NO_RESPONSE:
            LOG_DBG("GATTC_WRITE_NO_RESPONSE");
            break;

        case GATTC_SDP_DISC_SVC:
            LOG_DBG("GATTC_SDP_DISC_SVC");
            break;

        case GATTC_SDP_DISC_SVC_ALL:
        {
            LOG_DBG("GATTC_SDP_DISC_SVC_ALL");
            if (app_env.central.connect[index].ccc_handle_num != 0)
            {
                LOG_INF("Enable all notifications.");
                for (int i=0; i<app_env.central.connect[index].ccc_handle_num; i++)
                {
                    LOG_DBG("CCC handle: 0x%02x(%d)", app_env.central.connect[index].ccc_handle[i],
                                                      app_env.central.connect[index].ccc_handle[i]);
                    prf_gatt_write_ntf_ind(&app_env.central.prf_env, conidx,
                                           app_env.central.connect[index].ccc_handle[i],
                                           PRF_CLI_START_NTF);
                }
            }
        } break;

        default:
            break;
    }
    return (KE_MSG_CONSUMED);
}

/**
 ***************************************************************************************************
 * @brief Handles reception of the @ref GATTC_READ_IND message.
 * Generic event received after every simple read command sent to peer server.
 *
 * @param[in] msgid Id of the message received (probably unused).
 * @param[in] param Pointer to the parameters of the message.
 * @param[in] dest_id ID of the receiving task instance (probably unused).
 * @param[in] src_id ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ***************************************************************************************************
 */
__STATIC int app_gattc_read_ind_handler(ke_msg_id_t const msgid,
                                        struct gattc_read_ind const *param,
                                        ke_task_id_t const dest_id,
                                        ke_task_id_t const src_id)
{
    LOG_VBS_FUNC();

    return (KE_MSG_CONSUMED);
}

/**
 ***************************************************************************************************
 * @brief Handles reception of the @ref GATTC_EVENT_IND message.
 *
 * @param[in] msgid Id of the message received (probably unused).
 * @param[in] param Pointer to the parameters of the message.
 * @param[in] dest_id ID of the receiving task instance (probably unused).
 * @param[in] src_id ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ***************************************************************************************************
 */
__STATIC int app_gattc_event_ind_handler(ke_msg_id_t const msgid,
                                         struct gattc_event_ind const *param,
                                         ke_task_id_t const dest_id,
                                         ke_task_id_t const src_id)
{
    LOG_VBS_FUNC();

    if(param->type == GATTC_NOTIFY)
    {
    }

    return (KE_MSG_CONSUMED);
}


/**
 ***************************************************************************************************
 * @brief GATTC message default handler
 *
 * @param[in] msgid     Id of the message received.
 * @param[in] param     Pointer to the parameters of the message.
 * @param[in] dest_id   ID of the receiving task instance (TASK_GATTC).
 * @param[in] src_id    ID of the sending task instance.
 *
 * @return If the message was consumed or not.
 ***************************************************************************************************
 */
__STATIC int app_gattc_msg_dflt_handler(ke_msg_id_t const msgid,
                                      void const *param,
                                      ke_task_id_t const dest_id,
                                      ke_task_id_t const src_id)
{
    // Drop the message
    return (KE_MSG_CONSUMED);
}

/*
 * LOCAL VARIABLE DEFINITIONS
 ***************************************************************************************************
 */
// Default State handlers definition
const struct ke_msg_handler app_gattc_msg_handler_list[] =
{
    // Note: first message is latest message checked by kernel so default is put on top.
    {KE_MSG_DEFAULT_HANDLER,        (ke_msg_func_t)app_gattc_msg_dflt_handler},
    {GATTC_SDP_SVC_IND,             (ke_msg_func_t)app_gattc_sdp_svc_ind_handler},
    {GATTC_CMP_EVT,                 (ke_msg_func_t)app_gattc_cmp_evt_handler},
    {GATTC_EVENT_IND,               (ke_msg_func_t)app_gattc_event_ind_handler},
    {GATTC_READ_IND,                (ke_msg_func_t)app_gattc_read_ind_handler},

};

const struct app_subtask_handlers app_gattc_handlers = APP_HANDLERS(app_gattc);

/*
 * GLOBAL FUNCTION DEFINITIONS
 ***************************************************************************************************
 */
uint16_t app_gattc_perfect_once_tx_length(uint16_t mtu, uint16_t mto, uint16_t char_len)
{
    uint16_t mtux = co_min(APP_GATTC_MTU_TO_NTF_WRTCMD_LEN(mtu), char_len);
    uint16_t mtox = APP_GATTC_MTO_TO_NTF_WRTCMD_LEN(mto);

    return (mtux > mtox) ? (mtox + mto * ((mtux-mtox) / mto)) : (mtux);
}

uint16_t app_gattc_send_data(uint8_t handle, uint8_t *pdata, uint16_t len)
{
    uint8_t index = app_central_find_by_conidx(0);

    uint16_t templen = app_gattc_perfect_once_tx_length(app_env.central.connect[index].mtu, 
                                                        app_env.central.connect[index].tx_len, 
                                                        APP_GATTC_UPLOAD_MAX_LEN);
    templen = co_min(len, templen);
    prf_gatt_write(&app_env.central.prf_env, 
                   app_env.central.connect[index].conidx,
                   handle, 
                   pdata, 
                   templen, 
                   GATTC_WRITE);
    return templen;
}

uint16_t app_gattc_send_data_no_resp(uint8_t handle, uint8_t *pdata, uint16_t len)
{
    uint8_t index = app_central_find_by_conidx(0);

    uint16_t templen = app_gattc_perfect_once_tx_length(app_env.central.connect[index].mtu, 
                                                        app_env.central.connect[index].tx_len, 
                                                        APP_GATTC_UPLOAD_MAX_LEN);
    templen = co_min(len, templen);
    prf_gatt_write(&app_env.central.prf_env, 
                   app_env.central.connect[index].conidx,
                   handle, 
                   pdata, 
                   templen, 
                   GATTC_WRITE_NO_RESPONSE);
    return templen;
}

void app_gattc_read_data_by_uuid(uint8_t *uuid, uint8_t uuid_len)
{
    prf_env_t prf_env = app_env.central.prf_env;
    uint8_t index = app_central_find_by_conidx(0);
    uint8_t conidx = app_env.central.connect[index].conidx;
    struct gattc_read_cmd * req = KE_MSG_ALLOC(GATTC_READ_CMD,
                                               KE_BUILD_ID(TASK_GATTC, conidx),
                                               prf_src_task_get(&prf_env, conidx),
                                               gattc_read_cmd);
    req->operation             = GATTC_READ_BY_UUID;
    req->nb                    = 1;
    req->req.by_uuid.start_hdl = 0x0001;
    req->req.by_uuid.end_hdl   = 0xFFFF;
    req->req.by_uuid.uuid_len  = uuid_len;
    memcpy(req->req.by_uuid.uuid, uuid, uuid_len);
    ke_msg_send(req);
}

void app_gattc_read_data_by_uuid_ex(uint8_t conidx, struct gattc_read_by_uuid *param)
{
    prf_env_t prf_env = app_env.central.prf_env;
    struct gattc_read_cmd * req = KE_MSG_ALLOC(GATTC_READ_CMD,
                                               KE_BUILD_ID(TASK_GATTC, conidx),
                                               prf_src_task_get(&prf_env, conidx),
                                               gattc_read_cmd);
    req->operation             = GATTC_READ_BY_UUID;
    req->nb                    = 1;
    req->req.by_uuid.start_hdl = param->start_hdl;
    req->req.by_uuid.end_hdl   = param->end_hdl;
    req->req.by_uuid.uuid_len  = param->uuid_len;
    memcpy(req->req.by_uuid.uuid, param->uuid, param->uuid_len);
    ke_msg_send(req);
}
#endif

/// @} APP
